package com.adv.ems.utils;

import com.adv.ems.db.entity.BaseDO;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class RedisUtil<K extends BaseDO> implements SmartInitializingSingleton {
    private final Map<String, Map<Long, K>> cache = new ConcurrentHashMap<>();
    private final Map<String, Long> expireKey = new ConcurrentHashMap<>();

    public void flush(Class cls) {
        String key = cls.getName();
        cache.remove(key);
    }

    public String set(K value) {
        if (value == null) {
            return null;
        }
        String key = value.getClass().getName();
        Map<Long, K> orDefault = cache.getOrDefault(key, new ConcurrentHashMap<>());
        orDefault.put(value.getId(), value);
        cache.put(key, orDefault);
        return key;
    }

    public K get(Class<K> cls, Long id) {
        String key = cls.getName();
        Map<Long, K> longKMap = cache.get(key);
        if (longKMap == null) {
            return null;
        }
        return longKMap.get(id);
    }

    public List<K> get(Class<K> cls) {
        String key = cls.getName();
        return new ArrayList<>(cache.getOrDefault(key, new ConcurrentHashMap<>()).values());
    }

    public Map<Long, K> getMap(Class<K> cls) {
        String key = cls.getName();
        return cache.getOrDefault(key, new ConcurrentHashMap<>());
    }

    public void del(Class<K> cls) {
        String key = cls.getName();
        cache.remove(key);
    }

    public void del(Class<K> cls, Long id) {
        String key = cls.getName();
        cache.getOrDefault(key, new ConcurrentHashMap<>()).remove(id);
    }

    public boolean setNx(String key, Long seconds) {
        if (key == null) {
            return false;
        }
        if (expireKey.get(key) != null) {
            return false;
        }
        expireKey.put(key, System.currentTimeMillis() + seconds * 1000);
        return true;
    }

    private void dealOutOfTimeKeys() {
        long l = System.currentTimeMillis();
        Set<String> strings = expireKey.keySet();
        for (String key : strings) {
            Long expire = expireKey.get(key);
            if (expire == null) {
                expireKey.remove(key);
                continue;
            }
            if (expire <= l) {
                expireKey.remove(key);
            }
        }
    }

    @Override
    public void afterSingletonsInstantiated() {
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                    dealOutOfTimeKeys();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
}
